/*
	g++ Timeout.cpp -o Timeout -lACE -std=c++11 -lrt
*/


#include "ace/Thread.h"
#include "ace/Synch.h"
#include <iostream>
#include <stdlib.h>
#include <ace/Thread_Manager.h>
#include <stack>
#include <string>
#include <ctime>
#include <queue>
#include <mutex>
#include <semaphore.h>
#include <ace/Event_Handler.h>
#include "ace/Timer_Queue.h" 
#include "ace/Reactor.h" 
#include <time.h>
using namespace std;

mutex queue_mutex;
sem_t fullqueue;
queue<string> QueueObjectID;

class Time_Handler : public ACE_Event_Handler 
{ 
	public: 
		int handle_timeout (const ACE_Time_Value &tv, const void *arg=0)
		{
			ACE_DEBUG ((LM_DEBUG, "Timed out!\n")); 
			time_t rawtime;
			struct tm * timeinfo;
			time ( &rawtime );
			timeinfo = localtime ( &rawtime );
			string now =  asctime (timeinfo) ;
			queue_mutex.lock();
			QueueObjectID.push(now);
			queue_mutex.unlock();
			sem_post(&fullqueue);
			return 0; 
		} 
};

static ACE_THR_FUNC_RETURN timework(void *arg)
{
	Time_Handler * timer = new Time_Handler();
	ACE_Time_Value initialDelay (2);
	ACE_Time_Value interval (5);
	ACE_Reactor::instance()->schedule_timer (timer, 0, initialDelay, interval);
	while(1)
	{
		ACE_Reactor::instance()->handle_events();
	}
}
static void* worker(void *arg)
{
	queue<string> *QueueObjID = (queue<string> *) arg;
	while(true)
	{
		string strID;
		queue_mutex.lock();
		if(QueueObjID->empty())
		{
			//wait signal
			queue_mutex.unlock();
			ACE_DEBUG ((LM_DEBUG, "wait!\n\n")); 
			sem_wait(&fullqueue);
			continue;
		}
		else
		{
			strID = QueueObjID->front();
			QueueObjID->pop();
		}
		ACE_DEBUG((LM_DEBUG, "Thread (%t) : %s\n", strID.c_str()));
		queue_mutex.unlock();
		usleep(1000);
	}
	pthread_exit((void *)"Success");
}

int main(int argc, char *argv[])
{
	sem_init(&fullqueue, 0, 0);
	QueueObjectID.push("One");
	QueueObjectID.push("Two");
	QueueObjectID.push("Three");
	QueueObjectID.push("Four");
	QueueObjectID.push("Five");
	QueueObjectID.push("Six");
	
	ACE_Thread_Manager::instance()->spawn(timework,NULL);

	int n_threads= 5;
	ACE_thread_t *threadID = new ACE_thread_t[n_threads+1];
	ACE_hthread_t *threadHandles = new ACE_hthread_t[n_threads+1];
	if(ACE_Thread::spawn_n(threadID, n_threads,(ACE_THR_FUNC)worker,(void *)&QueueObjectID, THR_JOINABLE | THR_NEW_LWP,ACE_DEFAULT_THREAD_PRIORITY,0, 0, threadHandles)==-1)
	{
		ACE_DEBUG((LM_DEBUG, "Thread (%t) Done! Error in spawning thread\n"));
		return -1;
	}

	for(int i = 0; i < n_threads; i++)
	{
		ACE_Thread::join(threadHandles[i]);
	}
	ACE_Thread_Manager::instance()->wait();
}
